package scheduler;



/**
 * A stub for your second scheduler
 */
public class FavouriteSLSScheduler extends Scheduler {

	/**
	 * @see scheduler.Scheduler#authorsAndStudentIDs()
	 */
	public String authorsAndStudentIDs() {
		// TODO Your Code Here!
		return "Brandon Yuh 28402055\r\nShang Ling Wu 65226060";
	}

	/**
	 * @throws Exception 
	 * @see scheduler.Scheduler#schedule(scheduler.SchedulingInstance)
	 */
	public ScheduleChoice[] solve(SchedulingInstance pInstance) throws Exception {
		//Simulated annealing is used here
		//It performs better than Greedy Descent with Restarts because at the beginning, the algorithm is allowed to make unoptimal choices.
		ScheduleChoice[] bestScheduleFound = null;
		int fewestViolatedConstraints = Integer.MAX_VALUE;

		ScheduleChoice[] localBestScheduleFound = null;
		int localFewestViolatedConstraints = Integer.MAX_VALUE;


		localBestScheduleFound = initAndRandomize(localBestScheduleFound,pInstance);
		bestScheduleFound = initAndRandomize(bestScheduleFound,pInstance);
		localBestScheduleFound = bestScheduleFound.clone();
		fewestViolatedConstraints = evaluator.violatedConstraints(pInstance, bestScheduleFound);
		localFewestViolatedConstraints = fewestViolatedConstraints;
		double T = 1000;
		double Tmin = 0;
		double coolingFactor = 0.9;
		
		while( T > Tmin && !timeIsUp() && evaluator.violatedConstraints(pInstance, bestScheduleFound)>0 ){
			for(int classIndex = 0; classIndex < localBestScheduleFound.length; classIndex++){
				//localBestScheduleFound[classIndex]

				int prevRoom = localBestScheduleFound[classIndex].room;
				int prevTime = localBestScheduleFound[classIndex].timeslot;

				localBestScheduleFound[classIndex].room = r.nextInt(pInstance.numRooms);
				localBestScheduleFound[classIndex].timeslot = r.nextInt(pInstance.numTimeslots);
				int newViolations = evaluator.violatedConstraints(pInstance, localBestScheduleFound);
				if( Math.exp((localFewestViolatedConstraints-newViolations)/T) > r.nextDouble() ){
					localFewestViolatedConstraints = newViolations;
				}
				else{
					localBestScheduleFound[classIndex].room = prevRoom;
					localBestScheduleFound[classIndex].timeslot = prevTime;
				}

				//Save as best if it is better
				if(fewestViolatedConstraints > localFewestViolatedConstraints){
					bestScheduleFound = localBestScheduleFound.clone();
					fewestViolatedConstraints = localFewestViolatedConstraints;
				}
				T = T*coolingFactor;
			}
		}
		return bestScheduleFound;
	}

	private ScheduleChoice[] initAndRandomize(ScheduleChoice[] schedules, SchedulingInstance pInstance){
		schedules = new ScheduleChoice[pInstance.numCourses];
		for(int courseIndex = 0; courseIndex<pInstance.numCourses; courseIndex++ ){
			schedules[courseIndex] = new ScheduleChoice();
			schedules[courseIndex].room = r.nextInt(pInstance.numRooms);
			schedules[courseIndex].timeslot = r.nextInt(pInstance.numTimeslots);
		}

		return schedules;
	}

}
